import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Vector;

public class LogGenerator {

	public static Properties props ;
	
	public static String nameOutFile = "";
	public static int numberOutLines = 0 ;
	public static long startTime ;
	public static long endTime ;
	
	public static int sessionsAverage = 10 ;
	public static int sessionsUsers = 1000 ;
	public static int sessionsDuration = 10 ;
	public static double ratioSesiones = 0.0 ;
	
	public static Vector<UserSession> vtSessions = new Vector<UserSession>() ; 
	
	public static SimpleDateFormat sdfDay = new SimpleDateFormat("yyyy-MM-dd") ;
	public static SimpleDateFormat sdfDayHour = new SimpleDateFormat("yyyy-MM-dd_HH:mm") ;
	private static SimpleDateFormat sdfOut = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss") ;
	
	public static Vector<Hashtable<Integer, String>> vtHashTables = new Vector<Hashtable<Integer, String>>() ; 
	public static int maxHashSize = 0 ;

	public static int POS_USER = 0 ; 
	public static int POS_IP   = 1 ;	
	public static int POS_SESSION = 2 ;	
	public static int POS_NODE_ID = 3;	
	public static int POS_NET_ID = 4;	
	public static int POS_START_DATE = 5 ;	
	public static int POS_END_DATE = 6 ;	
	public static int POS_METHOD = 7 ;	
	public static int POS_HOST = 8 ;	
	public static int POS_URL = 9 ;	
	public static int POS_URL_REFERER = 10 ;	
	public static int POS_USER_AGENT = 11 ;	
	public static int POS_MIME_TYPE = 12 ;	
	public static int POS_HTTP_CODE = 13 ;
	
	public class UserSession {
		public String USER = "" ;
		public String SESSION = "" ;
	}
	
	public class LogEntry {
		UserSession SES ;
//		String USER ;	
		String IP ;	
//		String SESSION ;	
		String NODE_ID ;	
		String NET_ID ;	
		Date START_DATE ;	
		Date END_DATE ;	
		String METHOD ;	
		String HOST ;	
		String URL ;	
		String URL_REFERER ;	
		String USER_AGENT ;	
		String MIME_TYPE ;	
		String HTTP_CODE ;
		
		String to_String() {
//			if (1==1)
//			 return String.format( "%d\n", (END_DATE.getTime() - START_DATE.getTime()))  ;
			
			return String.format( "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", 
					SES.USER ,	
					IP ,	
					SES.SESSION ,	
					NODE_ID ,	
					NET_ID ,
					sdfOut.format( START_DATE ) ,	
					sdfOut.format( END_DATE ) ,	
					METHOD ,	
					HOST ,	
					URL ,	
					URL_REFERER ,	
					USER_AGENT ,
					MIME_TYPE ,	
					HTTP_CODE 
					) ;
		}

		public void generate(int numRecord, long t0) {
			int position = (int) Math.floor( Math.random()* maxHashSize )  ;

			assignSession(numRecord) ;
			IP 			= vtHashTables.get(POS_IP).get(position);	
//			SESSION 	= "sess~sess";	
			NODE_ID 	= vtHashTables.get(POS_NODE_ID).get(position);	
			NET_ID 		= vtHashTables.get(POS_NET_ID).get(position);
			START_DATE  = new Date(t0);	
			END_DATE    = new Date(t0 + (long) (Math.random()*2000*sessionsAverage) ) ;	
			METHOD 		= vtHashTables.get(POS_METHOD).get(position);	
			HOST  		= vtHashTables.get(POS_HOST).get(position);
			URL 		= vtHashTables.get(POS_URL).get(position);	
			URL_REFERER = vtHashTables.get(POS_URL_REFERER).get(position);
			USER_AGENT 	= vtHashTables.get(POS_USER_AGENT).get(position);
			MIME_TYPE 	= vtHashTables.get(POS_MIME_TYPE).get(position);
			HTTP_CODE 	= vtHashTables.get(POS_HTTP_CODE).get(position);
		}

		private void assignSession(int numRecord) {
			boolean addSesion = false ;
			boolean applyChanges = false ;

			double aux = Math.random() * sessionsAverage ;
			
			if ( vtSessions.size() <= 10 || numRecord%10 == 0 ) {
				applyChanges = true ;
			}
			
			if (vtSessions.size() <= aux || vtSessions.size() <= 10 ) {
				addSesion = true ;
			} else {
				addSesion = false ;
			}

			int posicion ;
			if (applyChanges) {
				if (addSesion ) {
					UserSession s = new UserSession() ;
					s.USER = String.format("ani%d", (int) (Math.random()*sessionsUsers) ) ;
					s.SESSION = String.format("%05d%05d~%05d%05d", 
							(int) (Math.random()*99999),
							(int) (Math.random()*99999),
							(int) (Math.random()*99999),
							(int) (Math.random()*99999)) ;
					vtSessions.add(s) ;
				} else {
					posicion = (int) Math.floor( Math.random() * vtSessions.size() ) ;
					vtSessions.remove(posicion) ;
				}
			}

			posicion = (int) Math.floor( Math.random() * vtSessions.size() ) ;
			
			SES 		= vtSessions.get(posicion) ;	
		}
	}

	public static void main(String[] args) {
		
		if (args.length < 4) {
			printUsage() ;
		}
		
		for (int t=0; t< 14; t++) {
			vtHashTables.add( new Hashtable<Integer, String>() ) ;
		}
		
		loadConfigFile() ;
		loadParameters(args) ;
		loadSampleFile() ;
		
		generate() ;
	}

	private static void generate() {
		LogGenerator le = new LogGenerator();
		LogEntry lg = le.new LogEntry() ;
		

		double t0 = startTime ;
		try {
			BufferedOutputStream fout = new BufferedOutputStream(new FileOutputStream(nameOutFile)) ;
			long runStartTime = System.currentTimeMillis() ;

			int auxLimitLines = 95*numberOutLines/100 ;
			long auxRatioSessions = -1 ;
			for (int index = 0 ; index < numberOutLines; index++) {
			  	if (index%20000 == 0 ) {
			  		printEstimated(index, numberOutLines, runStartTime) ;
			  	}
				lg.generate(index, (long) t0) ;
				
				if (index< auxLimitLines) {
					t0 += 2*Math.random()*ratioSesiones ;
				} else {
					if (auxRatioSessions < 0 ) {
						auxRatioSessions = (endTime - (long) t0) / ( numberOutLines - index ) ;
					}
					t0 += auxRatioSessions ;
				}
				
				fout.write( lg.to_String().getBytes() ) ;
				if (index%500 == 0 ) {
					fout.flush() ;
				}
			}
			fout.close();
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(-1) ;
		}
		
		System.out.println(sdfOut.format(t0)) ;
		
	}

	private static void printEstimated(int contador, int maxLines, long t0) {
		String estimado ="" ;
	  		  double porcentaje = Math.round(  ((double) (contador)*10000)/ (double) maxLines  ) / (double) 100 ;
	  		  String cadena = String.format( "%04.2f", porcentaje) ;

	  		  	long tActual = (System.currentTimeMillis() - t0) /1000 ;
//		  		  double segsEstimados = ((double) tActual )*(100-porcentaje) / porcentaje ;
		  		  int segsEstimados = (int) (((double) tActual )*(100-porcentaje) / porcentaje) ;
		  		  int segs = segsEstimados%60 ;
		  		  int aux = ( segsEstimados - segs ) / 60;
		  		  int mins = aux %60 ;
		  		  aux = (aux - mins) / 60 ;
		  		  int horas = aux ;
		  		  estimado = String.format("   (estimated: %02d:%02d:%02d)",
		  				  horas,
		  				  mins,
		  				  segs) ;

		  	  cadena += estimado ;
	  		  System.out.print ( cadena ) ;
	  		  for (int t=0; t<cadena.length(); t++) {
	  			  System.out.print('\b');
	  		  }
	}
	
	private static void printUsage() {
		System.out.println("Usage LogGenerator outfile number_of_lines startDate(yyyy-mm-dd_hh:mi) endDate(yyyy-mm-dd_hh:mi)");
		System.exit(-1) ;
	}

	private static void loadParameters(String[] args) {
		Date d ;
		nameOutFile = args[0] ;
		try {
			numberOutLines = Integer.parseInt(args[1]) ;
		} catch (Exception ex) {}
		
		if (numberOutLines <= 0 ) {
			System.out.println( "Parameter 2 number of lines is incorrect: " + args[1] );
			printUsage() ;
		}

		try {
			if (args[2].length()==10) {
				d = sdfDay.parse(args[2]) ;
			} else {
				d = sdfDayHour.parse(args[2]) ;
			}
			startTime = d.getTime() ;
		} catch (Exception e) {
			System.out.println( "Parameter 3 StartTime is incorrect:" + args[2]);
			printUsage() ;
		}

		try {
			if (args[3].length()==10) {
				d = sdfDay.parse(args[3]) ;
			} else {
				d = sdfDayHour.parse(args[3]) ;
			}
			endTime = d.getTime() ;
			if (args[3].length()==10) {
				endTime += 3600*24*1000 ;
			}
		} catch (ParseException e) {
			System.out.println( "Parameter 4 EndTime is incorrect" + args[3] );
			printUsage() ;
		}

		try {
			sessionsAverage = Integer.parseInt( props.getProperty( "sessions.average" ) ) ;
		} catch ( Exception ex) {}

		try {
			sessionsUsers = Integer.parseInt( props.getProperty( "sessions.users" ) ) ;
		} catch ( Exception ex) {}

		try {
			sessionsDuration = Integer.parseInt( props.getProperty( "sessions.duration" ) ) ;
		} catch ( Exception ex) {}
		
		ratioSesiones = (endTime - startTime  ) / numberOutLines ;
		
		System.out.println(
				String.format("Start Time: %s\n" +
							  "End Time:   %s\n",
							  sdfOut.format( new Date(startTime)),
							  sdfOut.format( new Date(endTime))
				)
		);
		
	}

	private static void loadConfigFile() {
		props = new Properties();
		InputStream in = props.getClass().getResourceAsStream("LogGenerator.properties");
		if (in == null) {
			try {
				in = new FileInputStream( new File("LogGenerator.properties") );
			} catch (FileNotFoundException e) { }
		}
				
		if (in == null) {
			try {
				in = new FileInputStream( new File("D:/Felipe/descarga/Desarrollos Java/Eclipse/Felipe_Personal/Iberdrola/src/LogGenerator.properties") );
			} catch (FileNotFoundException e) { }
		}

		try {
			if (in == null) {
				throw new Exception ("LogGenerator.properties not found");
			}
			props.load(in);
			in.close() ;		
		} catch (Exception e) {
			e.printStackTrace() ;
			System.exit(-1) ;
		}
	}

	private static void loadSampleFile() {
		
		String filename = props.getProperty("samplefile.name") ;
		int maxlines = 100000 ;
		
		try {
			maxlines = Integer.parseInt(props.getProperty("samplefile.maxlines") ) ;
		}catch (Exception ex) {} 
		
		if (filename != null) {
			try {
				System.out.println(String.format( "loading file %s max lines %d", 
						filename,
						maxlines));
			  FileInputStream fstream = new FileInputStream(filename);
			  DataInputStream in = new DataInputStream(fstream);
			  BufferedReader br = new BufferedReader(new InputStreamReader(in));

			  String strLine;
			  int counter = 0 ;
			  while ((strLine = br.readLine()) != null && counter < maxlines)   {
//				  System.out.println (strLine);
				  String fields[] = strLine.split("\t") ;
				  if ( fields.length == 14 ) {
					  for (int index = 0 ; index < 14 ; index++) {
						  vtHashTables.get(index).put(maxHashSize, fields[index]) ;
					  }
					  maxHashSize++ ;
				  }
				  counter++ ;
			  }
			  //Close the input stream
			  in.close();
			    } catch (Exception e){//Catch exception if any
			    	System.err.println("Error: " + e.getMessage());
			    	System.exit(-1) ;
			  }

//			  for (int index = 0 ; index < 14 ; index++) {
//				  System.out.println( index + " - " + vtHashTables.get(index)) ; ;
//			  }

		} else {
			System.out.println("Parameter samplefile is mandatory in config file");
			System.exit(-1) ;
		}
	}

}
